#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

#define N 128

typedef struct node
{
    struct sockaddr_in addr;
    struct node *next;
} linklist_t;

typedef struct
{
    char mtype;
    char mname[N];
    char mtext[N];
} msg_t;

linklist_t *create_empty_linklinst_head()
{
    linklist_t *h = (linklist_t *)malloc(sizeof(linklist_t));
    memset(&h->addr, 0, sizeof(h->addr)); // 清空结构体 addr
    h->next = NULL;
    return h;
}

int process_login(int sockfd, msg_t *msgp, linklist_t *h, struct sockaddr_in *addr)
{
    int ret;
    strcpy(msgp->mtext, "login");
    linklist_t *p = h->next; // p指向头结点的下一个节点(头结点不参与运算)
    while (p != NULL)
    {
        if (memcmp(&p->addr, addr, sizeof(*addr)) != 0) // 除了自己(addr)以外的所有地址
        {
            ret = sendto(sockfd, msgp, sizeof(msg_t), 0, (struct sockaddr *)&p->addr, sizeof(p->addr));
            if (ret < 0)
            {
                perror("sendto");
                exit(-1);
            }
        }
        p = p->next; // 移动节点
    }

    // 把新登录的成员 加入到链表
    p = (linklist_t *)malloc(sizeof(linklist_t)); // 申请一个节点
    p->addr = *addr;                              // 赋值
    p->next = h->next;
    h->next = p;

    return 0;
}

int process_chat(int sockfd, msg_t *msgp, linklist_t *h, struct sockaddr_in *addr)
{
    int ret;

    linklist_t *p = h->next; // p指向头结点的下一个节点(头结点不参与运算)

    while (p != NULL)
    {
        if (memcmp(&p->addr, addr, sizeof(*addr)) != 0) // 除了自己(addr)以外的所有地址
        {
            ret = sendto(sockfd, msgp, sizeof(msg_t), 0, (struct sockaddr *)&p->addr, sizeof(p->addr));
            if (ret < 0)
            {
                perror("sendto");
                exit(-1);
            }
        }
        p = p->next; // 移动节点
    }

    return 0;
}

int process_quit(int sockfd, msg_t *msgp, linklist_t *h, struct sockaddr_in *addr)
{
    int ret;

    strcpy(msgp->mtext, "quit");
    linklist_t *p = h->next; // p指向头结点的下一个节点(头结点不参与运算)
    linklist_t *q;           //用来保存要删除的节点

    while (p != NULL)
    {
        if (memcmp(&p->addr, addr, sizeof(*addr)) != 0) // 除了自己(addr)以外的所有地址
        {
            ret = sendto(sockfd, msgp, sizeof(msg_t), 0, (struct sockaddr *)&p->addr, sizeof(p->addr));
            if (ret < 0)
            {
                perror("sendto");
                exit(-1);
            }
        }
        p = p->next; // 移动节点
    }

    // 从链表中删除 退出的成员
    // p 要定位到 删除节点的前一个节点
    p = h; // p 定位到头结点, 就是地址信息的前一个节点

    while (p->next != NULL)
    {
        if (memcmp(&p->next->addr, addr, sizeof(*addr)) == 0)
        {
            // 删除节点
            q = p->next;       // 保存要删除的节点
            p->next = q->next; // 接链表
            free(q);
            return 0; // 函数直接返回即可 , 这个不能省略, 省略后会造成最后一个节点崩溃
        }
        p = p->next; // 移动节点
    }
}

int main(int argc, char const *argv[])
{
    int ret, sockfd;
    struct sockaddr_in server_addr, client_addr;
    char buf[N] = {0};
    socklen_t addrclen = sizeof(client_addr);

    if (argc != 3)
    {
        fprintf(stderr, "错误：运行程序时请带入参数(./06-udp_server ip port)/n");
        exit(-1);
    }

    msg_t msg;
    memset(&msg, 0, sizeof(msg)); // 清空结构体

    //用户数据包，使用udp协议
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0)
    {
        perror("sockfd");
        exit(-1);
    }

    printf("sockdf =%d\n", sockfd);

    memset(&server_addr, 0, sizeof(server_addr));     // 清空结构体
    memset(&client_addr, 0, sizeof(client_addr));     // 清空结构体
    server_addr.sin_family = AF_INET;                 // 什么类型的通信
    server_addr.sin_port = htons(atoi(argv[2]));      // 设置socket 的固定端口
    server_addr.sin_addr.s_addr = inet_addr(argv[1]); // 把字符串ip转换成网络二进制的ip地址

    ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (ret < 0)
    {
        perror("bind");
        exit(-1);
    }

    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork");
        exit(-1);
    }

    else if (pid == 0) //子进程
    {
        msg.mtype = 'C';
        strcpy(msg.mname, "server");

        while (1)
        {
            printf("请输入要广播的内容>:");
            fgets(msg.mtext, N, stdin);
            msg.mtext[strlen(msg.mtext) - 1] = 0; // 去掉'\n',  0 和'\0' , 和字符0 不一样 '0'=
            ret = sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
            if (ret < 0)
            {
                perror("sendto");
                exit(-1);
            }
        }
    }

    else //父进程
    {
        linklist_t *H = create_empty_linklinst_head();

        while (1)
        {
            ret = recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr *)&client_addr, &addrclen);
            if (ret < 0)
            {
                perror("recvfrom");
                exit(-1);
            }

            printf("%s&%d:%c\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), msg.mtype);

            switch (msg.mtype)
            {
            case 'L':
                process_login(sockfd, &msg, H, &client_addr);
                break;
            case 'C':
                process_chat(sockfd, &msg, H, &client_addr);
                break;
            case 'Q':
                process_quit(sockfd, &msg, H, &client_addr);
                break;
            default:
                break;
            }
        }
    }

    return 0;
}
